﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate;
using NHibernate.Criterion;

namespace Domain
{
    public class Repository<T> where T:class
    {
        public virtual String CamposDescripcion { get; set; }
        public virtual String CampoBajaLogica { get; set; }
        public virtual String CamposOrden { get; set; }
        public virtual String CampoEstado { get; set; }

#region Generic Repository Members

        #region Propiedades.
        #endregion
        
        /// <summary>
        /// Metodo generico que se utiliza para grabar cualquier entidad del sistema. 
        /// </summary>
        /// <param name="entity">
        ///     Entidad que se quiere guardar. 
        ///     En caso de ser una inserción, en la misma entidad devuelve el Id, 
        ///     ya que la misma fue pasada por referencia.
        /// </param>
        /// <returns>
        ///     Retorna true o false en caso de exito o fracaso de 
        ///     la operación.
        /// </returns>
        public bool Save(ref T entity)
        {
            Boolean exito = true;
            try { Conexion.GetSession.Save(entity); }
            catch (Exception e)
            {
                exito = false;
                //TODO
                //Dejar en un archivo de log del servidor el error que se produjo. 
                //Lanzar excepción o devolver mensaje de error. 
                throw e;
            }
            return exito;
        }

        /// <summary>
        /// Metodo generico que se utiliza para actualizar cualquier entidad del sistema. 
        /// </summary>
        /// <param name="entity">
        ///     Entidad que se quiere actualizar. 
        /// </param>
        /// <returns>
        ///     Retorna true o false en caso de exito o fracaso de 
        ///     la operación.
        /// </returns>
        public Boolean Update(ref T entity)
        {
            Boolean exito = true;
            try { Conexion.GetSession.Update (entity); }
            catch (Exception e)
            {
                exito = false;
                //TODO
                //Dejar en un archivo de log del servidor el error que se produjo. 
                //Lanzar excepción o devolver mensaje de error. 
                throw e;
            }
            return exito;
        }

        /// <summary>
        /// Metodo generico que se utiliza para borrar cualquier entidad del sistema. 
        /// Como consideración especial hay que tener en cuenta que la baja de una entidad
        /// puede ser lógica. Para configurar si la baja es logica o no se debe utilizar (o no),
        /// el atributo CampoBajaLógica. Si el mismo es nulo, o "", enotnces se asume que la baja 
        /// es física... sino se hace un update para actualizar dicho campo. 
        /// </summary>
        /// <param name="entity">
        ///     Entidad que se quiere borrar (ya sea logica o fisicamente). 
        /// </param>
        /// <returns>
        ///     Retorna true o false en caso de exito o fracaso de 
        ///     la operación.
        /// </returns>
        public Boolean Remove(T entity)
        {
            Boolean exito = true;
            try 
            {
                Type entityType = typeof(T);
                object ObjetoEntidad = Activator.CreateInstance(entityType);
                if (entity.GetType().GetField(CampoBajaLogica) == null)
                    Conexion.GetSession.Delete(entity);
                else 
                    Conexion.GetSession.Update(entity);
            }
            catch (Exception e)
            {
                exito = false;
                //TODO
                //Dejar en un archivo de log del servidor el error que se produjo. 
                //Lanzar excepción o devolver mensaje de error. 
                throw e;
            }
            return exito;
        }

        /// <summary>
        /// Recupera de la base de datos un objeto de la entidad, cuyo id es el que se le pasa como 
        /// parametro.
        /// NO sirve para el caso de entidades con pk compuestas... las cuales deveriamos de evitar. 
        /// </summary>
        /// <param name="id">Id. de la entidad que se desea recuperar.</param>
        /// <returns>
        ///     Devuelve la Entidad buscada, siempre que la misma exista.
        ///     En caso contrario la Entidad se devolverá vacía. 
        /// </returns>
        public T Get(object id)
        {
            try { return Conexion.GetSession.Get<T>(id);}
            catch (Exception e)
            {
                //TODO
                //Dejar en un archivo de log del servidor el error que se produjo. 
                //Lanzar excepción o devolver mensaje de error. 
                //Ver si no es preferible devolver true o flase y recibir la entity por parametro referencial.
                throw e;
            }
        }

        /// <summary>
        /// Busca los objetos donde el campo descripcion like anywhere value  
        /// </summary>
        /// <param name="value">Cadena que se utiliza en la comparacion.</param>
        /// <returns>
        ///     Retorna una lista de los objetos que se recuperaron segun el valor que se 
        ///     paso como parametro ordenados por el campo descripcion de la entidad. 

        /// </returns>
        public IList<T> Get(String value)
        {
            ICriteria criteria = Conexion.GetSession.CreateCriteria(typeof(T))
                                .Add(Restrictions.Like(CamposDescripcion, value, MatchMode.Anywhere))
                                .AddOrder(Order.Asc (CamposOrden));
            return criteria.List<T>() as List<T>;
        }

        /// <summary>
        /// Recupera de la base de datos todos los objetos de la entidad, devolviendo los mismos en una lista. 
        /// </summary>
        /// <typeparam name="T">Entidad sobre la cual el metodo tiene que operar.</typeparam>
        /// <returns>Retorna una lista con todos los objetos de la entidad determinada.</returns>
        public IList<T> GetAll<T>() //where T : class
        {
            try
            {
                ICriteria criteria = Conexion.GetSession.CreateCriteria(typeof(T));
                return criteria.List<T>();
            }
            catch (Exception e)
            {
                //TODO
                //Dejar en un archivo de log del servidor el error que se produjo. 
                //Lanzar excepción o devolver mensaje de error. 
                //Ver si no es preferible devolver true o flase y recibir la entity por parametro referencial.
                throw e;
            }
        }

        //TODO, analizar si es necesario este metodo.  
        public T GetProxy(object id)
        {
            return Conexion.GetSession.Load<T>(id);
        }

        public virtual Repositorio.EstadoRepository Estado() 
        { 
            return new Repositorio.EstadoRepository();
        }

        public virtual Repositorio.PeopleRepository People()
        {
            return new Repositorio.PeopleRepository();
        }

#endregion

    }
}